package cn.yeziji.forum.dao.impl;

import cn.yeziji.forum.annoataion.RemoveCache;
import cn.yeziji.forum.annoataion.SaveCache;
import cn.yeziji.forum.common.key.CommentCacheKey;
import cn.yeziji.forum.common.key.DetailsCacheKey;
import cn.yeziji.forum.converter.CommentConverter;
import cn.yeziji.forum.dao.base.ForumCommentBaseDao;
import cn.yeziji.forum.dao.intf.ForumCommentDao;
import cn.yeziji.forum.dao.iquery.CommentQuery;
import cn.yeziji.forum.dto.comment.ForumDetailCommentDTO;
import cn.yeziji.forum.entity.ForumCommentEntity;
import cn.yeziji.forum.entity.ForumDetailsEntity;
import cn.yeziji.forum.entity.ForumUserEntity;
import cn.yeziji.forum.mapper.ForumCommentMapper;
import cn.yeziji.forum.mapper.ForumDetailsMapper;
import cn.yeziji.forum.mapper.ForumUserMapper;
import cn.yeziji.forum.utils.CacheUtils;
import cn.yeziji.forum.utils.JwtOperaUtils;
import cn.yeziji.forum.vo.comment.DetailCommentVO;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 论坛评论逻辑实现层
 *
 * @author gzkemays
 * @since 2022/03/28 20:59
 */
@Repository
public class ForumCommentDaoImpl extends ForumCommentBaseDao implements ForumCommentDao {
  @Resource ForumCommentMapper commentMapper;
  @Resource ForumDetailsMapper detailsMapper;
  @Resource ForumUserMapper userMapper;
  @Resource HttpServletRequest request;
  @Resource CacheUtils cache;
  CommentConverter commentConverter = CommentConverter.INSTANCE;

  @Override
  @Transactional(rollbackFor = {Exception.class})
  @RemoveCache(
      value = {DetailsCacheKey.CACHE_GET_DETAIL_KEY, CommentCacheKey.DETAIL_COMMENT_CACHE_KEY},
      jsonKey = "detailId",
      joinery = "::",
      blur = false)
  public int commentDetail(ForumDetailCommentDTO dto) {
    Integer userId = JwtOperaUtils.getUserIdByRequest(request);
    dto.setUserId(userId);
    ForumCommentEntity entity = CommentConverter.INSTANCE.detailDtoConverterEntity(dto);
    ForumUserEntity user = userMapper.findById(userId);
    entity.setNickname(user.getNickname());
    entity.setAvatar(user.getAvatar());
    commentMapper.insert(entity);
    // 更新 detail 的 comment number
    Integer detailId = dto.getDetailId();
    ForumDetailsEntity detail = detailsMapper.findById(detailId);
    int insNumber = detail.getCommentNumber() + 1;
    detail.setCommentNumber(insNumber);
    detailsMapper.updateById(detail);
    cache.remove(CommentCacheKey.DETAIL_COMMENT_CACHE_KEY + detailId);
    return insNumber;
  }

  @Override
  @SaveCache(
      value = CommentCacheKey.DETAIL_COMMENT_CACHE_KEY,
      joinery = "::",
      joinReqParam = true,
      time = 30,
      unit = TimeUnit.MINUTES)
  public List<DetailCommentVO> detailCommentList(Integer detailId) {
    Integer userId = JwtOperaUtils.getUserIdIfZero(request);
    List<DetailCommentVO> commentsCache =
        cache.get(CommentCacheKey.DETAIL_COMMENT_CACHE_KEY + detailId);
    if (commentsCache == null || commentsCache.isEmpty()) {
      commentsCache =
          getCommentList(
              commentMapper.listMaps(CommentQuery.getDetailCommentListMapsQuery(detailId, userId)));
    }
    return commentsCache;
  }

  /**
   * 根据当前文章评论截取出其父子评论列表
   *
   * @param entities 当前文章的所有评论
   * @return 父子评论列表
   */
  private List<DetailCommentVO> getCommentList(List<Map<String, Object>> entities) {
    if (!CollectionUtils.isEmpty(entities)) {
      Set<Object> userIds = entities.stream().map(e -> e.get("userId")).collect(Collectors.toSet());
      List<ForumUserEntity> users = userMapper.listByIds(userIds);
      entities =
          entities.stream()
              .peek(
                  e -> {
                    for (ForumUserEntity user : users) {
                      if (e.get("userId").equals(user.getId())) {
                        e.put("avatar", user.getAvatar());
                      }
                    }
                  })
              .collect(Collectors.toList());
      // 过滤出顶级评论
      List<Map<String, Object>> superComments =
          entities.stream()
              .filter(entity -> Objects.deepEquals(entity.get("parentCommentId"), 0))
              .collect(Collectors.toList());
      // 过滤出子级评论
      // TODO: 没有做分页
      List<DetailCommentVO> elsetComments =
          getElsetComments(
              entities.stream()
                  .filter(entity -> !Objects.deepEquals(entity.get("parentCommentId"), 0))
                  .collect(Collectors.toList()));

      AtomicInteger tempFollow = new AtomicInteger(0);
      return superComments.stream()
          .map(
              comment -> {
                DetailCommentVO detailCommentVO =
                    commentConverter.mapConverterToDetailCommentVo(comment);
                List<DetailCommentVO> superSunList = new ArrayList<>();
                for (DetailCommentVO elsetComment : elsetComments) {
                  // 一级评论
                  if (elsetComment.getParentCommentId().equals(comment.get("id"))) {
                    superSunList.add(elsetComment);
                    addFollow(tempFollow, elsetComment.getDetailCommentVos());
                    tempFollow.addAndGet(elsetComment.getFollow().intValue());
                  }
                }
                detailCommentVO.setDetailCommentVos(superSunList);
                detailCommentVO.setFollow(tempFollow.longValue());
                tempFollow.set(0);
                return detailCommentVO;
              })
          .collect(Collectors.toList());
    }
    return null;
  }

  private void addFollow(AtomicInteger i, List<DetailCommentVO> vos) {
    if (vos != null && !vos.isEmpty()) {
      for (DetailCommentVO vo : vos) {
        int follow = vo.getFollow().intValue() == 0 ? 1 : vo.getFollow().intValue();
        i.addAndGet(follow);
        addFollow(i, vo.getDetailCommentVos());
      }
    }
  }
  /**
   * 递归获取最终子级
   *
   * <p>遍历子列表，而后对子孙进行获取
   *
   * @param sunEntities 子级评论集合
   */
  private List<DetailCommentVO> getElsetComments(List<Map<String, Object>> sunEntities) {
    List<DetailCommentVO> detailCommentVos = new ArrayList<>();
    for (Map<String, Object> sunEntity : sunEntities) {
      List<Map<String, Object>> elsetList = new ArrayList<>();
      for (int i = sunEntities.size() - 1; i >= 0; i--) {
        Integer elsetParentId = (int) sunEntities.get(i).get("parentCommentId");
        if (elsetParentId.equals(sunEntity.get("id"))) {
          elsetList.add(sunEntities.get(i));
        }
      }
      DetailCommentVO detailCommentVO = commentConverter.mapConverterToDetailCommentVo(sunEntity);
      detailCommentVO.setDetailCommentVos(
          commentConverter.mapConverterToDetailCommentVos(elsetList));
      detailCommentVO.setFollow(Long.parseLong(String.valueOf(elsetList.size())));
      detailCommentVos.add(detailCommentVO);
    }
    List<Integer> delIds = new ArrayList<>();
    for (DetailCommentVO detailCommentVo : detailCommentVos) {
      List<DetailCommentVO> elsetList = detailCommentVo.getDetailCommentVos();
      if (!CollectionUtils.isEmpty(elsetList)) {
        List<DetailCommentVO> realElsetList =
            elsetList.stream()
                .peek(
                    realElset -> {
                      for (DetailCommentVO commentVo : detailCommentVos) {
                        Integer id = realElset.getId();
                        if (id.equals(commentVo.getId())) {
                          delIds.add(id);
                          BeanUtils.copyProperties(commentVo, realElset);
                        }
                      }
                    })
                .collect(Collectors.toList());
        detailCommentVo.setDetailCommentVos(realElsetList);
      }
    }
    if (!detailCommentVos.isEmpty()) {
      List<DetailCommentVO> realDetailComments = new ArrayList<>();
      for (DetailCommentVO detailCommentVo : detailCommentVos) {
        if (!delIds.contains(detailCommentVo.getId())) {
          realDetailComments.add(detailCommentVo);
        }
      }
      return realDetailComments;
    }
    return detailCommentVos;
  }
}
